# -*- coding: utf8 -*-

__author__ = 'sergey'

import math
import traceback
from time import sleep, time
import gobject
from datetime import datetime
from threading import Thread, Event, Lock
from multiprocessing import cpu_count

class FindDupImages2(Thread):
    """
    __init__ : args = ( app, ui, db_req )
    """

    app = None
    ui = None
    lock_ui = None
    lock_stat = None

    db_req = None

    stop_event = None

    log = None

    t_begin = 0


    img_manager = None
    img_req = None
    stat_req = None


    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, verbose=None):
        self.app, self.ui, self.db_req = args
        self.stop_event = Event()
        self.lock_ui = Lock()
        self.lock_stat = Lock()
        Thread.__init__(self, group, target, name, args, kwargs, verbose)

    def run(self):
        from logger import get_file_logger
        self.log = get_file_logger("worker-FindDupImages")
        try:
            self.stop_event.clear()
            self.find_dupimages()
        except Exception as e:
            self.log.error(e)
            self.log.error(traceback.format_exc())
        self.db_req.close()
        return 0


    def match_func(self, model, iter, data):
        column, key = data # data is a tuple containing column number, key
        value = model.get_value(iter, column)
        return value == key

    def search(self, model, iter, func, data):
        while iter:
            if func(model, iter, data):
                return iter
            result = self.search(model, model.iter_children(iter), func, data)
            if result: return result
            iter = model.iter_next(iter)
        return None


    def format_seconds(self, seconds):

        s = int(seconds)

        m = (s - s % 60) / 60
        s %= 60
        h = (m - m % 60) / 60
        m %= 60
        d = (h - h % 24) / 24
        h %= 24

        return "%d дня, %02d:%02d:%02d" % (d, h, m, s,)


    def update_ui(self):

        t_now = time()

        itemNow = {
            "time": t_now,
            "proc": self.stats_req.get("search_dupimages_processed"),
            "similar": self.stats_req.get("search_dupimages_similar"),
            "count": self.stats_req.get("search_dupimages_count")
        }

        if itemNow["proc"] > 0:
            sum_seconds = (t_now - self.t_begin) * (itemNow["count"] * 1.0 / itemNow["proc"] - 1)
        else:
            sum_seconds = 0


        speed, sum_seconds = self.app._timed_stats_update("dupimages", itemNow["proc"], itemNow["count"])

        eta = self.format_seconds( sum_seconds )

        if itemNow["count"] > 0:
            p = itemNow["proc"] * 1.0 / itemNow["count"]
        else:
            p = 0
        if p < 0:
            p = 0
        if p > 1:
            p = 1

        if itemNow["count"]:
            s = math.sqrt(itemNow["similar"]) / itemNow["count"]
        else:
            s = 0
        if s < 0:
            s = 0
        if s > 1:
            s = 1

        gobject.idle_add( self.ui.get_object("progressbar_nonuniq_images").set_fraction, p )
        gobject.idle_add(
            self.ui.get_object("progressbar_nonuniq_images").set_text,
            "Обработано: %.3f из %d (%0.3f%%); Похожих: %d (%0.3f%%)" % (
                itemNow["proc"], itemNow["count"], p * 100,
                itemNow["similar"], s * 100
            )
        )
        gobject.idle_add( self.ui.get_object("label_images_eta").set_text, eta )

        title = self.ui.get_object("window1").get_title()
        new_title = "[ %.2f %%, %.2f %% ], %s - изображения" % ( p * 100, s * 100, self.app.get_ui_title(), )
        if title != new_title:
            gobject.idle_add( self.ui.get_object("window1").set_title, new_title )

        return not self.stop_event.isSet()


    def getImageRequest(self, number):
        self.img_manager.start_service(number)
        if not self.img_req:
            self.img_req = self.img_manager.get_request()
        return self.img_req


    def find_dupimages(self):

        self.log.info("START")

        self.app._timed_stats_begin()

        # from worker.search_dupimages_vectors import FindDupImagesVectors

        from service.search_dupimage_vector import SearchDupImageVectorManager

        self.stats_req = self.app.get_stats()

        task_manager = self.app.getTaskManager()

        self.img_manager = SearchDupImageVectorManager( (task_manager.host, task_manager.port,) )
        img_req = self.img_manager.get_request()

        gobject.idle_add( self.ui.get_object("label_images_eta").set_text, "Clean images dup table..." )

        self.log.debug("Clean images dup table...")
        self.db_req.clean_image2image()
        self.db_req.clean_file2file()

        gobject.idle_add( self.ui.get_object("label_images_eta").set_text, "Get images count..." )

        self.log.debug("Get images count...")
        t1 = datetime.now()

        images_count = self.db_req.get_images_count()

        t2 = datetime.now()
        self.log.debug("Got images count: %s, in %s" % (images_count, t2 - t1))

        gobject.idle_add( self.ui.get_object("label_images_eta").set_text, "Got images count: %s" % images_count )

        self.t_last = self.t_begin = time()

        self.stats_req.set("search_dupimages_count", images_count)
        self.stats_req.set("search_dupimages_processed", 0)
        self.stats_req.set("search_dupimages_similar", 0)

        if not images_count:
            self.log.info("STOP")
            return 0

        self.t_begin = time()

        gobject.timeout_add( 200, self.update_ui )

        offset = 0
        count = 50

        self.log.debug("Get offset=%d, count=%d" % (offset, count,))

        t1 = datetime.now()
        items = list(self.db_req.get_images(count, offset))
        t2 = datetime.now()
        self.log.debug("Got images in %s" % (t2 - t1,))

        threads_count = cpu_count()

        while not self.stop_event.isSet():
            try:
                item = items.pop()
            except:
                item = None

            # self.log.debug("Item=%r" % item)
            # print("-- item: %r" % (item,))
            if item is None:
                if offset + count >= images_count:
                    break
                offset += count
                self.log.debug("Get offset=%d, count=%d" % (offset, count,))
                t1 = datetime.now()
                try:
                    items = list(self.db_req.get_images(count, offset))
                except Exception as e:
                    self.log.error("Got error: %s" % e)
                    offset -= count
                    items = []
                finally:
                    t2 = datetime.now()
                    self.log.debug("Got in %s" % (t2 - t1,))

                continue

            added = False
            while not added:
                if self.stop_event.isSet():
                    break
                for i in range(threads_count):
                    if self.stop_event.isSet():
                        break
                    num = "iw-%s" % (i % threads_count)
                    req = self.getImageRequest(num)
                    if req.empty(num):
                        req.process(num, item)
                        added = True
                        break
                sleep(0.01)

        self.log.info("Wait while threads processed all images...")

        if not self.stop_event.isSet():
            self.stop_event.set()
            self.ui.get_object("button_find_dupimages").set_active(False)

            empty = False
            while not empty:
                empty = True
                for i in range(threads_count):
                    num = "iw-%s" % (i % threads_count)
                    req = self.getImageRequest(num)
                    if not req.empty(num):
                        empty = False
                if self.stop_event.isSet():
                    empty = True
                sleep(0.01)

        self.log.info("Stop threads")
        img_req.quit()

        self.log.info("Wait threads to stop...")
        while img_req.ping_all():
            sleep(0.1)

        self.log.info("STOP")

        return
